package com.example.netty.common.client;

import cn.hutool.core.util.ObjectUtil;
import com.example.netty.common.NettyThreadFactory;
import com.example.netty.common.utils.NettyConstants;
import com.example.netty.delimiter.client.DelimiterClientInitializer;
import com.example.netty.fixlength.client.FixLengthClientInitializer;
import com.example.netty.lengthfield.client.LengthFieldClientInitializer;
import com.example.netty.linebase.client.LineBaseClientInitializer;
import com.example.netty.nbcb.client.NormalNBCBClientInitializer;
import com.example.netty.normal.bytebuf.client.NormalByteBufClientInitializer;
import com.example.netty.normal.bytes.client.NormalByteClientInitializer;
import com.example.netty.normal.string.client.NormalStringClientInitializer;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

/**
 * <p></p>
 *
 * @author xin
 * @version 2023/11/2 15:39
 **/
@Component
public class NettyClient {

    private static final Logger logger = LoggerFactory.getLogger(NettyClient.class);
    private final EventLoopGroup group = new NioEventLoopGroup(new NettyThreadFactory("client"));
    private final Bootstrap bootstrap = new Bootstrap();
    private ChannelFuture channelFuture;

    @Async
    public void init(String type) {
        bootstrap.group(group)
                .channel(NioSocketChannel.class)
                .option(ChannelOption.TCP_NODELAY, true)
                .option(ChannelOption.SO_KEEPALIVE, true);

        if (NettyConstants.NORMAL_BYTE_BUF.equalsIgnoreCase(type)) {
            logger.debug("+++++++初始化normal byte buf client ChannelInitializer ++++++++++++");
            bootstrap.handler(new NormalByteBufClientInitializer());
        } else if (NettyConstants.NORMAL_BYTE.equalsIgnoreCase(type)) {
            logger.debug("+++++++初始化normal byte client ChannelInitializer ++++++++++++");
            bootstrap.handler(new NormalByteClientInitializer());
        } else if (NettyConstants.NORMAL_STRING.equalsIgnoreCase(type)) {
            logger.debug("+++++++初始化normal string client ChannelInitializer ++++++++++++");
            bootstrap.handler(new NormalStringClientInitializer());
        }else if (NettyConstants.NBCB.equalsIgnoreCase(type)) {
            logger.debug("+++++++初始化粘包、拆包 client ChannelInitializer ++++++++++++");
            bootstrap.handler(new NormalNBCBClientInitializer());
        }else if (NettyConstants.FIX_LENGTH.equalsIgnoreCase(type)) {
            logger.debug("+++++++初始化粘包、拆包 固定长度解决方案 client ChannelInitializer ++++++++++++");
            bootstrap.handler(new FixLengthClientInitializer());
        }else if (NettyConstants.LINE_BASE.equalsIgnoreCase(type)) {
            logger.debug("+++++++初始化粘包、拆包 通过在包尾添加回车换行符 \\r\\n 来区分整包消息解决方案 client ChannelInitializer ++++++++++++");
            bootstrap.handler(new LineBaseClientInitializer());
        }else if (NettyConstants.DELIMITER.equalsIgnoreCase(type)) {
            logger.debug("+++++++初始化粘包、拆包 特殊字符作为分隔符来区分整包消息解决方案 client ChannelInitializer ++++++++++++");
            bootstrap.handler(new DelimiterClientInitializer());
        }else if (NettyConstants.LENGTH_FIELD.equalsIgnoreCase(type)) {
            logger.debug("+++++++初始化粘包、拆包 指定长度来标识整包消息，通过在包头指定整包长度来约定包长。解决方案 client ChannelInitializer ++++++++++++");
            bootstrap.handler(new LengthFieldClientInitializer());
        }

    }


    public ChannelFuture connect(String host, int port) {
        doConnect(host, port);
        return this.channelFuture;
    }

    public void doConnect(String host, Integer port) {
        if (ObjectUtil.isEmpty(host) || ObjectUtil.isEmpty(port)) {
            throw new RuntimeException("IP host 为null");
        }
        try {
            ChannelFuture future = bootstrap.connect(host, port);
            //休眠1秒，保证通道建立成功
            Thread.sleep(100);
            channelFuture = future;
            logger.info("ChannelFuture 创建状态{}", channelFuture.isSuccess());
        } catch (Exception e) {
            logger.error("client start fail", e);
        }
    }

    public void disconnect(Channel channel) {
        if (ObjectUtil.isNotNull(channel)) {
            try {
                ChannelFuture channelFuture = channel.close();
                channel.disconnect();
            } catch (Exception e) {
                logger.error("关闭通道异常", e);
            }
        }
    }
}
